package finder

import (
	"binseek/pkg/utils"
	"bytes"
	"fmt"
	"io"
	"os"
)

const (
	INDEX_POS_SIZE  = 4   // 开头索引固定4字节
	RECORD_MAX_SIZE = 256 // 最多读取字节
)

type ReadFlag byte

const (
	ReadOnward ReadFlag = iota + 1 // 连续读两段
	ReadForward
	ReadBackward = 254
	ReadAppendix = 255
)

type Finder interface {
	Find(target string) (info string, err error)
}

type Pandect struct { // 正文
	IsBom    bool
	FileSize int64
	JumpSize int
	Index    *Catalog
	Appendix Glossary
	*os.File
}

func NewPandect(fileName string, jumpSize int, isBom bool) (*Pandect, error) {
	var pand *Pandect
	fp, fileSize, err := utils.OpenFile(fileName)
	if err == nil {
		pand = &Pandect{File: fp, IsBom: isBom, FileSize: fileSize, JumpSize: jumpSize}
	}
	return pand, err
}

func (p *Pandect) Close() (err error) {
	if err = p.Index.Close(); err == nil {
		err = p.File.Close()
	}
	return
}

func (p *Pandect) ReadPositions(withAppendix bool) (posLst []int64, err error) {
	var data [][]byte
	sizes := []int{INDEX_POS_SIZE, INDEX_POS_SIZE}
	if withAppendix {
		sizes = append(sizes, INDEX_POS_SIZE)
	}
	data, err = utils.ReadMoreBytes(p.File, 0, sizes...)
	if err != nil && len(data) < len(sizes) {
		return
	}
	firstPos, lastPos := int64(utils.Uint64(data[0], p.IsBom)), int64(utils.Uint64(data[1], p.IsBom))
	if firstPos > lastPos { // 根据大小交换位置
		firstPos, lastPos = lastPos, firstPos
	}
	posLst = append(posLst, lastPos, firstPos)
	if withAppendix {
		posLst = append(posLst, int64(utils.Uint64(data[2], p.IsBom)))
	}
	return
}

func (p *Pandect) CreateIndex(fixSize, indexSize int, firstPos int64) (err error) {
	p.Index = NewCatalog(fixSize)
	err = p.Index.MemoryMap(int(p.File.Fd()), firstPos, indexSize)
	if err != nil {
		p.Index.Data, err = utils.ReadBytes(p.File, firstPos, indexSize)
	}
	return
}

func (p *Pandect) CreateAppendix(appendPos, firstPos int64) (err error) {
	var data []byte
	data, err = utils.ReadBytes(p.File, appendPos, int(firstPos-appendPos))
	if err == nil {
		p.Appendix = CreateGlossary(data)
	}
	return
}

func (p *Pandect) AheadRead(fixSize, flexSize int, mode ReadFlag) (int, []byte, error) {
	var err error
	n, i, j := 0, 0, 0
	data := make([]byte, fixSize+flexSize)
	n, err = p.Read(data)
	if flexSize == 0 || err != nil && err != io.EOF {
		return 0, data, err
	}
	data = data[:n]
	utils.DebugPrint("read", data)
	i = bytes.IndexByte(data[fixSize:], 0x00)
	if i < 0 || i > n {
		return i, data, nil
	}
	i += fixSize
	if mode == ReadOnward { // 读取第二段\0
		if j = bytes.IndexByte(data[i+1:], 0x00); j >= 0 {
			j += 1
		} else {
			j = p.JumpSize + 2 // 包括前面的\0
		}
	}
	return i, data[:i+j], nil
}

func (p *Pandect) JumpTo(absPosData, relPosData []byte, isBack bool) (err error) {
	var pos int64
	if utils.IsZeroBytes(relPosData) == false {
		pos = int64(utils.Uint64(relPosData, p.IsBom))
		if isBack {
			pos = 0 - pos
		}
	}
	seekMode, absPos := io.SeekStart, int64(utils.Uint64(absPosData, p.IsBom))
	if absPos == 0 {
		seekMode = io.SeekCurrent
	}
	if pos = pos + absPos; pos <= 8 {
		if pos != 0 {
			err = fmt.Errorf("error params: pos data %d", pos)
		}
		return err
	}
	utils.DebugPrint("pos", "0x"+utils.Bin2Hex(absPosData), "0x"+utils.Bin2Hex(relPosData), pos)
	if _, err = p.File.Seek(pos, seekMode); err != nil {
		err = fmt.Errorf("error params: file seek %d", pos)
	}
	return err
}

func (p *Pandect) JumpRead(posData []byte, fixSize, flexSize int, mode ReadFlag) ([]byte, error) {
	var err error
	if err = p.JumpTo(posData, []byte{0x00}, mode == ReadBackward); err != nil {
		return nil, err
	}
	var data []byte
	_, data, err = p.AheadRead(fixSize, flexSize, mode)
	return data, err
}

func (p *Pandect) SearchIndex(target []byte) (idxData []byte, err error) {
	if !p.IsBom {
		target = utils.ReverseBytes(target)
	}
	idx := BinSearch(p.Index, target, p.IsBom, false, false)
	if idx < 0 {
		return
	}
	utils.DebugPrint("target:", target, idx, p.Index.Count())
	idxData, err = p.Index.ReadIndex(idx, p.Index.fixSize)
	return
}
